; =====================================
; =====Maths Utility=====
; Formats:
; 32-Bit Unsigned Number
; [00000000] [00000000] [00000000] [00000000]
; Extended for fixed number formats:
; [00000000] [00000000].[00000000] [00000000]
; = 1/(2^16) of the original number, precision up to 1/(2^16), enough for adc related calculation
; instant form of extension used in calc with faster speed:
; [00000000].[00000000] [00000000]

; Add two 32-bit unsigned integer
; Input:
;   R1 - Address of first integer (Lowest bit)
;   R0 - the second
; Output:
;   RES32A
;   CY=1 When overflow
; Used Reg. R0 R1 R2 R3 R4 R5(Changed)
ADD32U:
			  MOV    A, @R1
			  ADD    A, @R0
			  MOV    RES32A, A
			  
			  MOV    R3, #RES32A;
			  MOV    R4, #03H  ; i=3
ADD32U_LOOP:  INC    R1
			  INC    R0
			  INC    R3
			  MOV    A, @R1
			  ADDC   A, @R0
			  
			 ;====MOV   @R3, A====   -- 51 DOES NOT SUPPORT
			 ; A -> R5
			  MOV    R5, A
			 
			 ;R0 -> A -> R2
			  MOV    A, R0
			  MOV    R2, A
			 ;R3 -> A -> R0
			  MOV    A, R3
			  MOV    R0, A
			 ;R5 -> A (restore data)
			  MOV    A, R5
			 ;Save DATA
			  MOV    @R0, A
			 ;R2 -> A -> R0
			  MOV    A, R2
			  MOV    R0, A
			 ;R5 -> A(useless)
			  ;MOV    A, R5
			  
			  DJNZ   R4, ADD32U_LOOP
			  
			  RET

; MUL16U -- Multiply two 16-bit integer
;        -- MUL16UI Frontend
; Result: RES32A
; Input: R0 - address of first number
;        R1 - the another
; Used Reg. R0 R1
MUL16U:		  MOV A, @R0
			  MOV R6, A
			  INC R0
			  MOV A, @R0
			  MOV R7, A
			  
			  MOV A, @R1
			  MOV R4, A
			  INC R1
			  MOV A, @R1
			  MOV R5, A
			  
			  MOV R0, #RES32A
			  SJMP MUL16UI

; MUL16UI -- Instant version
; Input: (R7R6) - (ab)
; 		 (R5R4) - (cd)
;		 (R0) - START ADDRESS OF THE RESULT (ab)*(cd)
; Output R0 will be the upper address of the result
; Used Reg. R2R3, R1(Carry)
MUL16UI:	  MOV A, R6
			  MOV B, R4
			  MUL AB     ;b*d, A=bdL, B=bdH
			  MOV @R0, A 
			  MOV R3, B  ;R3 = bdH
			  MOV A, R7
			  MOV B, R4
			  MUL AB     ;a*d, A=adL, B=adH
			  ADD A, R3  ;
			  MOV R3, A  ;R3 = adL+bdH
			  MOV A, B
			  ADDC A, #00H  ;adH + CY
			  MOV R2, A
			  MOV A, R6
			  MOV B, R5
			  MUL AB     ;b*c, A=bcL, B=bcH
			  ADD A, R3  ;bdH+adL+bcL
			  INC R0
			  MOV @R0, A ;second byte of product
			  ; POSSIBLE TO HAVE TWO CARRY
			  ; SAVE ONE OF THEM IN R1
			  MOV R1, #0 ;R1=0 
			  MOV A, R2
			  ADDC A, B
			  MOV R2, A
			  JNC MUL16UI_NEXT
			  INC R1
MUL16UI_NEXT: MOV A, R7
			  MOV B, R5
			  MUL AB
			  ADD A, R2
			  INC R0
			  MOV @R0, A
			  MOV A, B
			  ADDC A, R1
			  INC R0
			  MOV @R0, A
			  RET
			  
			  
; DIV16UI -- Instant version of DIV16U (General Division Program)
; Input : (R7R6) - Dividend (R5R4) - Divisor
; Output: (R7R6) - Quotient (R3R2) - Remainder
;       : PSW.5 = F0, DIVIDE_BY_ZERO SYMBOL
; Used Reg: R3R2(Remainder) R1 - Shift Count Reg. R0 as temporary diff reg.
DIV16UI:	  MOV A, R5
			  JNZ DIV16UI_START
			  MOV A, R4
			  JZ DIV16UI_OVER
DIV16UI_START:MOV R2, #00H
			  MOV R3, #00H
			  MOV R1, #10H
DIV16UI_DIV1: CLR C
			  MOV A, R6
			  RLC A
			  MOV R6, A
			  MOV A, R7
			  RLC A
			  MOV R7, A
			  MOV A, R2
			  RLC A
			  MOV R2, A
			  MOV A, R3
			  RLC A
			  MOV R3, A
			  MOV A, R2
			  SUBB A, R4
			  MOV R0, A
			  MOV A, R3
			  SUBB A, R5
			  JC DIV16UI_NEXT
			  INC R6
			  MOV R3, A
			  MOV A, R0
			  MOV R2, A
DIV16UI_NEXT: DJNZ R1, DIV16UI_DIV1
DIV16UI_DONE: CLR F0
			  RET
DIV16UI_OVER: SETB F0
			  RET
			  

; =======================
; DispInt8 -- Display 8-bit integer on LCD1602
;          -- 000 - 255
; Usage:
;   MOV R4, #00H -- StartPtr (MAKE SURE YOU HAVE ENOUGH SPACE!!)
;   MOV A, #FFH -- Int to display
;   LCALL DispInt8
DispInt8:	  
			  MOV    R5, #00H
			  MOV    R6, #00H
			  MOV    R7, #00H
			  JZ     DispInt8_Disp  ; A=0 -> 000
			  
			  MOV    B, #0AH
			  DIV    AB
			  MOV    R7, B
			  MOV    B, #0AH
			  DIV    AB
			  MOV    R6, B
			  MOV    R5, A
			  
			  
DispInt8_Disp:	; SET D7=1 (10000000B)
				XCH    A, R4
				ORL    A, #80H
				XCH    A, R4
				
				; ADD R5, '0' (#30H)
				XCH    A, R5
				ADD    A, #30H
				XCH    A, R5
				
				; ADD R6, '0'
				XCH    A, R6
				ADD    A, #30H
				XCH    A, R6
				
				; ADD R7, '0'
				XCH    A, R7
				ADD    A, #30H
				XCH    A, R7
				
			  MOV    DAT_PORT, R4
			  LCALL  WR_LCD_CMD
			  MOV    A, R5
			  LCALL  WR_LCD_DAT
			  
			  INC    R4
			  MOV    DAT_PORT, R4
			  LCALL  WR_LCD_CMD
			  MOV    A, R6
			  LCALL  WR_LCD_DAT
			  
			  INC    R4
			  MOV    DAT_PORT, R4
			  LCALL  WR_LCD_CMD
			  MOV    A, R7
			  LCALL  WR_LCD_DAT
			  
			  RET

; DispInt16I -- Display 16-Bit Integer(Instant) on LCD1602
; Input: (R7R6) - Integer (R4) - Cursor Position
; Used Reg. R0~R7 
; Used Mem TMP8A TMP8B TMP32A
; "00000" - "65535" is introduced
; TODO: SKIP LAST CYCLE OF DIVISION(IN VAIN)
DispInt16I:
			; Cursor + 4
			; SET D7=1 (10000000B)
				XCH    A, R4
				ORL    A, #80H
				ADD    A, #04H
				XCH    A, R4
			  MOV TMP8A, R4
			  MOV R0, #05H ; Loop count = 5

			  
D16I_LOOP:	  MOV TMP8B, R0

			  MOV R5, #00H ; Divisor Pre-set 
			  MOV R4, #0AH ; = 10
			  LCALL DIV16UI
			  ; R3, R2 As remainder
			  ; INSTANT DISP
			  MOV A, R2
			  ADD A, #30H
			  
			  MOV R3, TMP8A

			  MOV DAT_PORT, R3
			  LCALL WR_LCD_CMD
			  NOP 
			  LCALL WR_LCD_DAT
			  
			  DEC R3
			  MOV TMP8A, R3
			  
			  ; Loop Ctrl
			  MOV R0, TMP8B
			  DJNZ R0, D16I_LOOP
			
D16I_DONE:    RET			  
			  
; DispDec16I: Display decimal number
; 0.[R7: XXXXXXXX] [R6: XXXXXXXX]
; R4 as cursor position
; R0 as required digit
; USED TMP8A TMP8B TMP32A

DispDec16I:   ; SET D7=1 (10000000B)
				XCH    A, R4
				ORL    A, #80H
				XCH    A, R4
		      MOV TMP8A, R4
			  
DD16I_LOOP:	  MOV TMP8B, R0
			  
			  MOV R5, #0H
			  MOV R4, #0AH
			  MOV R0, #TMP32A
			  LCALL MUL16UI
			  
			  ; Put Results back
			  MOV R6, (TMP32A)
			  MOV R7, (TMP32A + 1)
			  
			  ; READ OUT RESULT (result is in tmp32a[0:3])
			  ; SINCE *TMP32A WILL ALWAYS BE ZERO
			  MOV R2, (TMP32A + 2)
			  
			  ; INSTANT DISP
			  MOV A, R2
			  ADD A, #30H
			  
			  MOV R3, TMP8A
			  
			  MOV DAT_PORT, R3
			  LCALL WR_LCD_CMD
			  NOP
			  LCALL WR_LCD_DAT
			  
			  INC R3
			  MOV TMP8A, R3
			  
			  ; Loop Ctrl
			  MOV R0, TMP8B
			  DJNZ R0, DD16I_LOOP
			  
DD16I_DONE:    RET		
			  
			  
; Dispstr -- Display String on LCD1602 (DOES NOT MEASURE SCR LENGTH)
; Used Reg. DPTR A R7 R6(Current Position)
; Usage:
; 	MOV DPTR, HelloStr
; 	LCALL DispStr
DispStr:    
			  MOV    A, #00H
			  MOVC   A, @A+DPTR		; Get Cursor Start Position
			  MOV    R7, A
			  
			  MOV    A, #01H
			  MOV    R6, A
   DSR_A:	  MOVC   A, @A+DPTR
			  JZ     DSR_B          ; Reached the end of the string
			  
			  ; = ORL    R7, #80H ; SET D7=1 (10000000B)
			  ; 51 DOES NOT SUPPORT THAT
				XCH    A, R7
				ORL    A, #80H
				XCH    A, R7
			  
			  MOV    DAT_PORT, R7
			  LCALL  WR_LCD_CMD
			  NOP
			  LCALL  WR_LCD_DAT
			  
			  ; = ANL    R7, #7FH  ; (01111111B)
			  ; 51 DOES NOT SUPPORT THAT
			    XCH    A, R7
				ANL    A, #7FH
				XCH    A, R7
				
			  INC    R7             ; NEW CURSOR POSITION
			  INC    R6             ; NEW ADDR 
			  MOV    A, R6
			  AJMP   DSR_A

   DSR_B:	  
			  RET